import json
import os
import uuid
from datetime import datetime

import jieba
import redis
import requests
from openai import OpenAI
from rest_framework.exceptions import AuthenticationFailed

from rest_framework.response import Response
from rest_framework.views import APIView

from boss import settings
from chat.FunctionCalling import verification_ducational
from chat.generate_resume_test import generate_word_resume
from user.models import User, JobInfo, WorkExperience
from utils.JWTTool import JwtUtils

import logging

# 使用chat应用的logger，对应settings中的chat logger配置
logger = logging.getLogger('chat')

# Create your views here.

client = redis.Redis(host="localhost", port=6380, db=10, decode_responses=True)


def verify_education(arguments):
    """
    1:验证学历的工具(梦远数据的API)
    """
    try:

        api_key = os.getenv("MY_XXW_BGCX_API_KEY")
        # 设置url
        url = 'https://www.apimy.cn/api/xxw/bgcx?key=' + api_key
        vcode = arguments["vcode"]

        # 发送post请求
        response = requests.post(url, data={'vcode': vcode})

        # 获取响应内容
        result = response.json()

        # 打印结果
        print(result)
        print(type(result))
        if result["code"] == 200:
            return json.dumps(result["data"], ensure_ascii=False)
        else:
            return f"学历验证失败:{result['msg']}"
    except Exception as e:
        print(f"学历验证失败:{e}")
        return "学历验证失败"


# 2:创建工具列表
tools = [
    {
        "type": "function",
        "function": {
            "name": "verify_education",
            "description": "通过学信网验证码查询并验证学历信息, 包括:姓名, 学校,专业等详细内容",
            "parameters": {
                "type": "object",
                "properties": {
                    "vcode": {
                        "type": "string",
                        "description": "学信网验证码"
                    }
                },
                "required": ["vcode"]
            }
        }
    }
]


def get_user_id(request):
    # 从Authorization header中获取token并解析用户ID
    auth_header = request.META.get('HTTP_AUTHORIZATION', '')
    logger.debug(f"接收到的Authorization header: {auth_header[:20]}...")  # 只记录前20个字符，保护隐私

    if not auth_header or not auth_header.startswith('Bearer '):
        logger.warning("未提供有效的认证Token")
        raise AuthenticationFailed('未提供有效的认证Token')

    token = auth_header.split(' ')[1]
    logger.debug(f"解析出的token: {token[:20]}...")  # 只记录前20个字符

    try:
        userid = JwtUtils.get_user_id_from_token(token)
        logger.debug(f"成功解析用户ID: {userid}")
        return userid
    except Exception as e:
        logger.error(f"Token解析失败: {str(e)}")
        raise AuthenticationFailed('Token无效')


def create_session(userid):
    """
    创建新会话的通用函数
    """
    logger.info(f"开始为用户 {userid} 创建新会话")

    try:
        # 生成唯一的sessionid
        session_id = str(uuid.uuid4())
        logger.debug(f"生成会话ID: {session_id}")

        # 初始化hash结构,会话元数据 session:{userId}:{seesionid}-->创建时间,空的最后的消息
        session_meta = {
            "session_id": session_id,
            "create_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "last_message": "",  # 最后一条消息,空的
        }

        # 保存会话元数据: 命名规则: 项目名:业务名:功能名:业务数据
        session_key = f"boss:chat:session:{userid}:{session_id}"
        client.hset(session_key, mapping=session_meta)
        logger.debug(f"保存会话元数据到Redis: {session_key}")

        # 初始化空的消息列表(List):message:u1:s1
        message_list_key = f"boss:chat:message:list:{userid}:{session_id}"

        init_system_message = {"role": "system", "content": "你是专业的人力资源专家"}
        # 添加消息到列表左侧
        # 将字典转化成字符串 序列化
        client.lpush(message_list_key, json.dumps(init_system_message))
        logger.debug(f"初始化系统消息到: {message_list_key}")

        # 将sessionid添加到用户的会话集合(Set结构)
        session_set_key = f"boss:chat:session:set:{userid}"
        client.sadd(session_set_key, session_id)

        # 为所有的键设置过期时间(30天)
        client.expire(session_key, 60 * 60 * 24 * 30)
        client.expire(message_list_key, 60 * 60 * 24 * 30)
        client.expire(session_set_key, 60 * 60 * 24 * 30)
        logger.debug(f"设置Redis键过期时间: 30天")

        logger.info(f"成功创建会话 - 用户: {userid}, 会话ID: {session_id}")
        return session_id, session_meta

    except Exception as e:
        logger.error(f"创建会话失败 - 用户: {userid}, 错误: {str(e)}", exc_info=True)
        raise


class CreateSessionAPIView(APIView):
    """
    创建会话的接口
    """

    def post(self, request):
        logger.info("收到创建会话请求")

        try:
            # 从Authorization header中获取token并解析用户ID
            userid = get_user_id(request)
            logger.debug(f"用户ID解析成功: {userid}")

            session_id, session_meta = create_session(userid)

            logger.info(f"会话创建成功 - 用户: {userid}, 会话ID: {session_id}")

            return Response({
                "code": 1,
                "message": "创建会话成功",
                "data": {
                    "session_id": session_id
                }
            })

        except AuthenticationFailed as e:
            logger.warning(f"用户认证失败: {str(e)}")
            return Response({
                "code": 0,
                "message": "认证失败"
            }, status=401)

        except Exception as e:
            logger.error(f"创建会话异常: {str(e)}", exc_info=True)
            return Response({
                "code": 0,
                "message": f"创建会话失败: {str(e)}"
            }, status=500)


class ListSessionAPIView(APIView):
    def get(self, request):
        try:
            # token = request.META.get('AUTHORIZATION')
            # userid = JwtUtils.get_user_id_from_token(token)
            # 1: 获取userid
            # userid = request.query_params.get('userid')
            userid = get_user_id(request)
            # 2: 从set结构获取该用户所有的session_id
            session_set_key = f"boss:chat:session:set:{userid}"
            session_ids = client.smembers(session_set_key)
            logger.debug(f"从redis里面获取到的会话ID集合:{session_ids}")
            sessions = []
            for session_id in session_ids:
                session_key = f"boss:chat:session:{userid}:{session_id}"
                session_meta = client.hgetall(session_key)
                sessions.append(session_meta)
            # 3: 对会话列表进行排序,按照创建时间倒序
            sessions.sort(key=lambda x: x['create_time'], reverse=True)
            return Response({
                "code": 1,
                "message": "获取会话列表成功",
                "data": {
                    "sessions": sessions
                }
            })
        except Exception as e:
            print(e)
            return Response({
                "code": 0,
                "message": f"获取会话列表失败:{str(e)}"
            })


class SessionMessageAPIView(APIView):
    """
    1:获取userid
    2:获取session_id
    3:验证session_id是否存在[通过hash结构验证]
    4:从list结构读取所有的消息
    5:返回的所有的消息
    """

    def get(self, request):
        try:
            # userid = request.query_params.get('userid')
            userid = get_user_id(request)
            session_id = request.query_params.get('session_id')

            session_key = f"boss:chat:session:{userid}:{session_id}"
            session_meta = client.hgetall(session_key)
            if not session_meta:
                return Response({
                    "code": 0,
                    "message": "会话不存在"
                })

            message_list_key = f"boss:chat:message:list:{userid}:{session_id}"
            # 反序列化: 将字符串转化成字典
            messages = [json.loads(msg) for msg in client.lrange(message_list_key, 0, -1)]
            messages.reverse()
            logger.info(f"用户ID:{userid},会话ID:{session_id},消息列表:{messages}")
            return Response({
                "code": 1,
                "message": "获取消息列表成功",
                "data": {
                    "messages": messages[1:]
                }
            })
        except Exception as e:
            print(e)
            return Response({
                "code": 0,
                "message": f"获取消息列表失败:{str(e)}"
            })


ai_client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)


def intent_recognition(question: str) -> str:
    # 判断意图: 是否   --》是生成简历
    jieba.load_userdict("/Users/baimu/PycharmProjects/2505A/boss-llm/boss/chat/boss_zhipin_dict.txt")
    question_keyword_list = list(jieba.cut(question))
    print(f"原始问题分词:{question_keyword_list}")

    generate_resume_word = ["简历", "简历生成", "生成简历", "导出简历", "下载简历", "生成一份简历"]
    for question_keyword in question_keyword_list:
        if question_keyword in generate_resume_word:
            logger.info("生成简历的意图----------------")
            return "generate_resume"

    return "other"


def generate_resume_prompt(user_id: int) -> str:
    education_dict = {
        1: '初中以下',
        2: '初中',
        3: '中专',
        4: '高中',
        5: '大专',
        6: '本科',
        7: '硕士',
        8: '博士及以上'
    }

    current_status_dict = {
        1: '在职看机会',
        2: '离职',
        3: '在校生等',
        4: '兼职'
    }
    # 根据user_id 进行ORM查询
    user = User.objects.get(id=user_id)
    user_phone = user.phone
    user_email = user.email
    logger.info(f"用户ID:{user_id},用户手机号:{user_phone},用户邮箱:{user_email}")
    logger.info(user)

    jobinfo = JobInfo.objects.filter(user=user).first()
    jobinfo_name = jobinfo.name
    jobinfo_sex = "女" if jobinfo.sex == 1 else "男"
    jobinfo_birthdate = jobinfo.birthdate
    jobinfo_description = jobinfo.description
    jobinfo_education = education_dict.get(jobinfo.education)
    jobinfo_current_status = current_status_dict.get(jobinfo.current_status)
    from user.models import WorkExperience
    workexperience_list = WorkExperience.objects.filter(job_info=jobinfo)

    workexperiences = []
    for workexperience in workexperience_list:
        workexperience_dict = {
            "workexperience_company": "",
            "workexperience_position": "",
            "workexperience_start_date": "",
            "workexperience_end_date": "",
            "workexperience_description": "",
            "workexperience_salary_range": "",
            "workexperience_industry": "",
        }
        workexperience_dict["workexperience_company"] = workexperience.company
        workexperience_dict["workexperience_position"] = workexperience.position
        workexperience_dict["workexperience_start_date"] = workexperience.start_date
        workexperience_dict["workexperience_end_date"] = workexperience.end_date
        workexperience_dict["workexperience_description"] = workexperience.description
        workexperience_dict["workexperience_salary_range"] = workexperience.salary_range
        workexperience_dict["workexperience_industry"] = workexperience.industry
        workexperiences.append(workexperience_dict)
    # workexperience_text = "\n".join(workexperiences)

    prompt = f"""
         ## 设定角色
             你是互联网行业资深简历优化专家，熟悉Python开发、产品经理等岗位的简历撰写逻辑，能精准提炼职业亮点。
         ##  任务描述
             生成一个"Python工程师"的简历,这个简历中需要包括以下内容:
             基本信息:姓名 性别 联系方式 邮箱 出生日期  求职意向 	简介 在职状态
             教育经历: 1-2段,每一个段有 学校名称 专业名称 入学毕业 毕业时间 最高学历
             工作经历: 1-2段, 公司名称 职位名称 入职时间 离职时间 工作描述
             项目经历: 5-7个项目,每一个项目有 项目名称 时间 项目描述 技术栈 业务描述 个人职责
             专业技能: 里面包括 编程工具 python基础 python底层 数据库 中间件 大模型技术  相关框架 前端技术  测试技术  运维技术
             自我评价:3句话,突出自我优势和亮点即可
         ##  输出格式	
             {{
    "base_info":{{
    "name":"{jobinfo_name}",
                     "sex":"{jobinfo_sex}",
                     "phone":"{user_phone}",
                     "email":"{user_email}",
                     "jobinfo_birthdate":"{jobinfo_birthdate}",
                     "job_objective":"字符串[如:python工程师]",
                     "jobinfo_description":"{jobinfo_description}",
                     "jobinfo_current_status":"{jobinfo_current_status}",
                 }},
                 "educational":[
                     {{
    "school_name":"字符串[如:北京理工大学]",
                         "professional_name":"字符串[如:计算机科学与技术]",
                         "enrollment_time":"字符串[如:2016年9月]",
                         "graduation_time":"字符串[如:2020年7月]",
                         "jobinfo_education":"{jobinfo_education}",
                     }}
                 ],
                 "work_experience":{workexperiences},
                 "project_experience":[
                     {{
    "project_name":"电商供应链库存管理系统",
                         "time":"2023 年 9 月 - 2024 年 3 月",
                         "project_description":"针对原有库存系统高并发下数据不一致、响应延迟问题，重构核心模块，实现库存预占、扣减、回滚的原子化操作，支撑大促期间日均 500 万 + 订单的库存处理需求。",
                         "technology_stack":"Python 3.10、FastAPI、MySQL 8.0（读写分离）、Redis Cluster（分布式锁 / 缓存）、Kafka（消息队列）、Docker、Kubernetes",
                         "business_description":"解决电商大促场景下库存超卖 / 少卖问题，提升库存数据准确性至 99.99%，接口平均响应时间从 200ms 降至 50ms，支撑双 11、618 等大促活动平稳运行。",
                         "personal_responsibility":"1. 独立设计库存预占核心算法，基于 Redis 分布式锁解决并发冲突；2. 优化 MySQL 索引与查询语句，拆分大表（从 1 亿行拆分为 10 个分表）；3. 编写单元测试与压测脚本（Locust），保障系统抗压能力达 5000 QPS；4. 对接采购、仓储团队需求，输出 API 文档并支持联调。"
                     }}
                 ],
                 "professional_skills":{{
    "programming_tools":"PyCharm、Git（GitLab/GitHub）、Jenkins（CI/CD）、Docker、Kubernetes、Locust（压测）、Postman（接口调试）、Grafana/Prometheus（监控）",
         "python_basic":"精通 Python 3.x 语法、数据结构（列表 / 字典 / 集合 / 堆）、算法（排序 / 查找 / 动态规划）；熟练掌握并发编程（多线程 threading、多进程 multiprocessing、协程 asyncio/aiohttp）；了解内存管理、异常处理、代码优化（Pylint/Black 规范）",
         "python_underlying":"理解 Python GIL 全局解释器锁原理及规避方案；熟悉 CPython 解释器工作流程（词法分析→语法分析→字节码→执行）；了解字节码指令、垃圾回收机制（引用计数 + 分代回收）；对比过 PyPy 与 CPython 性能差异及适用场景",
         "database":"精通关系型数据库 MySQL/PostgreSQL（索引优化、事务隔离级别、读写分离、分库分表）；熟练使用非关系型数据库 Redis（缓存、分布式锁、消息队列）、MongoDB（文档存储）；了解 OLAP 数据库 ClickHouse、时序数据库 InfluxDB；能编写高效 SQL 语句，解决慢查询问题",
         "middleware":"熟练使用消息队列 Kafka/RabbitMQ（生产者 - 消费者模型、分区 / 队列设计、消息重试）；熟悉搜索引擎 Elasticsearch（索引创建、DSL 查询、聚合分析）；了解 Nginx 反向代理、负载均衡配置；能基于中间件设计高可用、高并发系统架构",
         "large_model_technology":"熟悉大模型微调技术（LoRA/QLoRA）；掌握 LangChain 框架（链、代理、记忆）；了解 RAG 检索增强生成流程（文档加载→分割→embedding→向量存储→检索）；能使用 Prompt Engineering 优化问答效果；有 LLaMA/ChatGLM 模型应用开发经验",
         "relevant_frameworks":"Web 框架：精通 FastAPI（高性能异步）、Flask（轻量），熟悉 Django（全栈）；数据处理：熟练使用 Pandas/NumPy（数据清洗、分析）、PySpark（分布式计算）；ORM 框架：精通 SQLAlchemy；任务调度：熟悉 Airflow/Celery；测试框架：熟练使用 Pytest",
         "frontend_technology":"了解 HTML/CSS 基础语法；熟悉 JavaScript 基本操作；能使用 Vue.js 开发简单前端页面（如后台管理界面）；可独立完成前后端联调（对接 Axios/JSON 数据格式）"
                 }},
                 "self_evaluation":"字符串"
             }}
         """

    return prompt


def compress_message_history(messages):
    """
    messages:所有的消息 12
    """
    prompt = """
    请将以下历史对话压缩为简洁的摘要，保留关键信息和上下文逻辑，供继续对话使用。
    不需要逐句总结，重点提取讨论的主题、关键问题和重要结论。
    压缩后的内容控制在200字以内。
    历史对话:
    """
    logger.warning(f"所有的消息:{messages}")
    print(f"所有的消息长度:{len(messages)}")
    if len(messages) <= 10:
        return messages
    # 保留最近的10条消息[滑动窗口] 10
    recent_message = messages[-10:]

    # 需要压缩历史消息,第一条是系统消息 2
    early_message = messages[1:-10]
    print(f"需要压缩的历史消息:{early_message}")
    # 使用列表收集字符串然后一次性join，提高拼接效率
    history_text_parts = []
    for message in early_message:
        # user/system/assistant
        role = message["role"]
        content = message["content"]
        role_name = "用户" if role == "user" else "模型助手"
        # 修复字符串拼接逻辑
        history_text_parts.append(f"{role_name}: {content}")
    print(f"历史消息文本1:{history_text_parts}")
    history_text = "\n".join(history_text_parts)
    print(f"历史消息文本2:{history_text}")
    prompt += history_text

    print(f"最后完整的提示词:{prompt}")
    try:
        result = ai_client.chat.completions.create(
            model="qwen-plus",
            messages=[
                {
                    "role": "user",
                    "content": prompt
                }
            ],
            max_tokens=1024,
            temperature=0.6,
        )
        # 返回结果
        content = result.choices[0].message.content
        print(f"大模型压缩后的结果:{content}")
        # 压缩消息
        compress_message = [{"role": "system", "content": content}]
        return compress_message + recent_message
    except Exception as e:

        # 异常处理：如果AI调用失败，返回未压缩的消息
        print(f"AI压缩消息失败: {e}")
        return messages


# user_input = "学历验证:A7FKKV3C4Q40RWGD"
class ChatAPIView(APIView):
    def post(self, request):
        question = request.data.get('question')
        # userid = request.data.get('userid')
        userid = get_user_id(request)
        session_id = request.data.get('session_id')
        try:
            if not question:
                return Response({
                    "code": 0,
                    "message": "问题不能为空"
                })

            session_key = f"boss:chat:session:{userid}:{session_id}"
            session_meta = client.hgetall(session_key)
            if not session_meta:
                # 重新执行CreateSessionAPIView
                session_id, session_meta = create_session(userid)

            # 4: 把当前的问题, 作为用户的消息存到redis
            # 的list消息列表中
            # 4.1:初始化空的消息列表(List):message:u1:s1
            intent_recognition_res = intent_recognition(question)
            message_list_key = f"boss:chat:message:list:{userid}:{session_id}"
            user_message = {"role": "user", "content": question}
            # 4.2 添加消息到列表左侧
            client.lpush(message_list_key, json.dumps(user_message))
            if intent_recognition_res == "generate_resume":
                # 组装生成简历的prompt
                question = generate_resume_prompt(userid)

            messages = [json.loads(msg) for msg in client.lrange(message_list_key, 0, -1)]
            print(f"messages的类型:{type(messages)}")
            for message in messages:
                print(f"从redis中获取的消息:{message}")
                print(f"从redis中获取的消息🉐️类型:{type(message)}")

            messages.reverse()
            print(f"从redis中获取10条消息:{messages}")
            last_message = compress_message_history(messages)
            print(f"最后完整的消息列表:{last_message}")
            my_message = last_message + [{"role": "user", "content": question}]
            logger.info(f"大模型输入的提示词:{my_message}")

            result = ai_client.chat.completions.create(
                model="qwen-plus",
                messages=my_message,
                stream=False,
                temperature=0.7,
                tools=tools
            )

            logger.info(f"大模型输出的结果:{result}")
            logger.info("判断是否需要执行function calling 函数")
            assistant_output = result.choices[0].message

            # 如果需要function calling,则tool_calls不为空
            tool_calls = result.choices[0].message.tool_calls
            if assistant_output.content == "":
                assistant_output.content = ""
            messages.append(assistant_output)
            if assistant_output.content == "" and tool_calls is not None:
                logger.info("需要处理function calling...")
                for tool_call in tool_calls:
                    tool_call_id = tool_call.id
                    func_name = tool_call.function.name
                    func_arguments = tool_call.function.arguments
                    arguments = json.loads(func_arguments)
                    logger.info(f"正在调用工具 [{func_name}]，参数：{arguments}")
                    if func_name == "verify_education":
                        tool_result = verify_education(arguments)
                        logger.debug(f"学历工具返回结果:{tool_result}")
                        # 构造工具返回信息
                        tool_message = {
                            "role": "tool",
                            "tool_call_id": tool_call_id,
                            "content": tool_result,  # 保持原始工具输出
                        }
                        messages.append(tool_message)
                        completion = ai_client.chat.completions.create(
                            model="qwen-plus",
                            messages=messages,
                            stream=False,
                            temperature=0.7,
                            tools=tools
                        )
                        ai_reply = completion.choices[0].message.content
                        logger.info(f"经过function calling 最后给用户返回的结果:{ai_reply}")

            else:
                logger.info("不需要处理function calling...")
                ai_reply = result.choices[0].message.content
                print(f"大模型输出的回复:{ai_reply}")
                print(type(ai_reply))
                # logger.info(f"大模型输出的回复:{ai_reply}")
                # logger.info(f"大模型输出的回复--类型:{type(ai_reply)}")
                if intent_recognition_res == "generate_resume":
                    # logger.info(f"大模型输出的简历信息:{ai_reply}")
                    # ai_reply 是json格式的简历信息,转化成word
                    # 生成word, 上传到文件服务器(本地),最后要URL
                    # 把URL存到redis
                    ai_reply = ai_reply.strip("```json").strip("```")
                    resume_info = json.loads(ai_reply)
                    file_name, resume_name = generate_word_resume(resume_info)
                    logger.info(f"生成word文件成功:{file_name}")
                    file_path = f"{settings.DOMAIN}{settings.MEDIA_URL}{resume_name}"
                    logger.info(f"file_path:{file_path}")
                    ai_reply = "生成简历成功🆗:" + f"<a href='{file_path}'>请查看</a>"

            ai_message = {"role": "assistant", "content": ai_reply}
            logger.info(f"大模型输出的回复:{ai_message}")
            # 4.2 添加消息到列表左侧
            client.lpush(message_list_key, json.dumps(ai_message))

            session_key = f"boss:chat:session:{userid}:{session_id}"
            client.hset(session_key, "last_message", ai_reply[:20])
            client.hset(session_key, "create_time", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))

            new_messages = [json.loads(msg) for msg in client.lrange(message_list_key, 0, -1)]
            new_messages.reverse()
            return Response({
                "code": 1,
                "message": "获取回复成功",
                "data": {
                    "reply": ai_reply,
                    "messages": new_messages[1:],
                    "session_id": session_id
                }
            })
        except Exception as e:
            print(e)
            return Response({
                "code": 0,
                "message": f"获取回复失败:{str(e)}"
            })
